package com.example.clock.util;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * 日期操作工具类
 */
public class DateUtil {
    private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);

    /**
     * 日期转换-  String -> Date 【yyyy-MM-dd】
     *
     * @param dateString 字符串时间
     * @return Date类型信息
     * @throws Exception 抛出异常
     */
    public static Date parseString2Date(String dateString) {
        if (dateString == null) {
            return null;
        }
        return parseString2Date(dateString, "yyyy-MM-dd");
    }

    /**
     * 日期转换-  String -> Date
     *
     * @param dateString 字符串时间
     * @param pattern    格式模板
     * @return Date类型信息
     * @throws Exception 抛出异常
     */
    public static Date parseString2Date(String dateString, String pattern) {
        if (dateString == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Date date = null;
        try {
            date = sdf.parse(dateString);
        } catch (ParseException var4) {
            logger.error("parser exception:{}", var4);
        }
        return date;
    }

    /**
     * 日期转换 Date -> String yyyy-MM-dd
     *
     * @param date Date类型信息
     * @return 字符串时间
     * @throws Exception 抛出异常
     */
    public static String parseDate2String(Date date) {
        if (date == null) {
            return null;
        }
        return parseDate2String(date, "yyyy-MM-dd");
    }

    /**
     * 日期转换 Date -> String yyyy-MM-dd HH:mm:ss
     *
     * @param date Date类型信息
     * @return 字符串时间
     * @throws Exception 抛出异常
     */
    public static String parseDate2StringDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return parseDate2String(date, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 日期转换 Date -> String
     *
     * @param date    Date类型信息
     * @param pattern 格式模板
     * @return 字符串时间
     * @throws Exception 抛出异常
     */
    public static String parseDate2String(Date date, String pattern) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        String strDate = sdf.format(date);
        return strDate;
    }

//================LocalDateTime Stirng 互转=================================================

    /**
     * 日期转换 LocalDateTime -> String yyyy-MM-dd
     *
     * @param localDateTime LocalDateTime类型信息
     * @return 字符串时间
     * @throws Exception 抛出异常
     */
    public static String parseLocalDateTime2String(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return null;
        }
        return parseLocalDateTime2String(localDateTime, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 日期转换 LocalDateTime -> String yyyy-MM-dd
     *
     * @param localDateTime Date类型信息
     * @return 字符串时间
     * @throws Exception 抛出异常
     */
    public static String parseLocalDateTime2String(LocalDateTime localDateTime, String pattern) {
        if (localDateTime == null) {
            return null;
        }
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        String strDate = dateTimeFormatter.format(localDateTime);
        return strDate;
    }

    /**
     * 日期转换 LocalDate -> String yyyy-MM-dd
     *
     * @param localDate Date类型信息
     * @return 字符串时间
     * @throws Exception 抛出异常
     */
    public static String parseLocalDate2String(LocalDate localDate, String pattern) {
        if (localDate == null) {
            return null;
        }
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        String strDate = dateTimeFormatter.format(localDate);
        return strDate;
    }


    /**
     * 日期转换-  String -> LocalDateTime
     *
     * @param dateString 字符串时间
     * @return Date类型信息
     * @throws Exception 抛出异常
     */
    public static LocalDateTime parseString2LocalDateTime(String dateString) {
        return parseString2LocalDateTime(dateString, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 日期转换-  String -> LocalDateTime
     *
     * @param dateString 字符串时间
     * @param pattern    格式模板
     * @return Date类型信息
     * @throws Exception 抛出异常
     */
    public static LocalDateTime parseString2LocalDateTime(String dateString, String pattern) {
        if (dateString == null) {
            return null;
        }
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        LocalDateTime localDateTime = LocalDateTime.parse(dateString, dateTimeFormatter);
        return localDateTime;
    }

    //    ============================date 和localDate localDateTime 互转====================
    private static final ZoneId ZONE_ID = ZoneId.systemDefault();

    /**
     * LocalDateTime转化为Date
     *
     * @param localDateTime
     * @return
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZONE_ID).toInstant());
    }

    /**
     * LocalDateTime转化为Date
     *
     * @param localDate
     * @return
     */
    public static Date toDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZONE_ID).toInstant());
    }


    /**
     * Date转化为LocalDateTime
     *
     * @param date
     * @return
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZONE_ID);
    }

    /**
     * LocalDate转化为LocalDateTime
     *
     * @param localDate
     * @return
     */
    public static LocalDateTime toLocalDateTime(LocalDate localDate) {
        return LocalDateTime.of(localDate, LocalTime.MIN);
    }

    /**
     * Date转化为LocalDate
     *
     * @param date
     * @return
     */
    public static LocalDate toLocalDate(Date date) {
        return date.toInstant().atZone(ZONE_ID).toLocalDate();
    }

    /**
     * Date转化为LocalDate
     *
     * @param localDateTime
     * @return
     */
    public static LocalDate toLocalDate(LocalDateTime localDateTime) {
        LocalDate localDate = localDateTime.atZone(ZONE_ID).toLocalDate();
        return localDate;
    }

//========================获取当前日期时间=========================


    /**
     * 获取自定义时间字符串
     *
     * @param format 格式化类型
     * @return
     */
    public static String format(String format) {
        return parseDate2String(new Date(), format);
    }


    /**
     * 获取年月日 yyyy-MM-dd
     *
     * @return
     */
    public static String getCurrentDate() {
        return parseDate2String(new Date(), "yyyy-MM-dd");
    }

    /**
     * 获取年月日时分秒 yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    public static String getCurrentDateTime() {
        return parseDate2String(new Date(), "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 获取年月日时分秒毫秒 yyyy-MM-dd HH:mm:ss:SSS
     *
     * @return
     */
    public static String getCurrentDateTimeMill() {
        String datestr = parseDate2String(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
        return datestr;
    }

    /**
     * 获取年月日时分秒毫秒 yyyy-MM-dd HH:mm:ss:SSS
     *
     * @return 2023-03-11 17:04:25.93
     */
    public static Timestamp getCurrentTimestamp() {
        Calendar calendar = Calendar.getInstance();
//        calendar.set(1, 2090);
        return new Timestamp(calendar.getTimeInMillis());
    }

    /**
     * 将时间转换为时间戳
     *
     * @param dateStr yyyy-MM-dd HH:mm:ss
     * @return 1678525350000
     * @throws ParseException
     */
    public static String dateToStamp(String dateStr) throws ParseException {
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = simpleDateFormat.parse(dateStr);
        long ts = date.getTime();
        res = String.valueOf(ts);
        return res;
    }

//=======================================================================================

    /**
     * 获取当前日期的本周一的日期
     */
    public static Date getThisWeekMonday(Date date) {
//        Calendar cal = Calendar.getInstance();
//        cal.setTime(date);
//        // 获得当前日期是一个星期的第几天
//        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
//        if (1 == dayWeek) {
//            cal.add(Calendar.DAY_OF_MONTH, -1);
//        }
//        // 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
//        cal.setFirstDayOfWeek(Calendar.MONDAY);
//        // 获得当前日期是一个星期的第几天
//        int day = cal.get(Calendar.DAY_OF_WEEK);
//        // 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
//        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
//        return cal.getTime();
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setTime(date);
        c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
        return c.getTime();
    }


    /**
     * 获取当前日期的本周日的日期
     */
    public static Date getThisWeekSunday(Date date) {
        //国外已周日为第一天
//        Calendar c = Calendar.getInstance();
//        int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1;
//        if (dayOfWeek == 0) {
//            dayOfWeek = 7;
//        }
//        c.add(Calendar.DATE, 7 -dayOfWeek );
//        return c.getTime();

        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setTime(date);
        c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6);
        return c.getTime();
    }


    /**
     * 获得上周一的日期
     *
     * @param date
     * @return
     */
    public static Date geLastWeekMonday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getThisWeekMonday(date));
        cal.add(Calendar.DATE, -7);
        return cal.getTime();
    }

    /**
     * 获得上周一的日期
     *
     * @param date
     * @return
     */
    public static String getWeek(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("EEEE");
        Calendar cal = Calendar.getInstance();
        // 将日期设置为解析后的日期
        cal.setTime(date);
        // 获取星期
        return sdf.format(cal.getTime());
    }

    /**
     * 获得下周一的日期
     *
     * @param date
     * @return
     */
    public static Date getNextWeekMonday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getThisWeekMonday(date));
        cal.add(Calendar.DATE, 7);
        return cal.getTime();
    }

    /**
     * 获得本月一日的日期
     *
     * @return
     */
    public static Date getFirstDay4ThisMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }

    /**
     * 获得本月最后一日的日期
     *
     * @return
     */
    public static Date getLastDay4ThisMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, 0);
        return calendar.getTime();
    }


//================


    /**
     * 是否在30分钟之后
     *
     * @param date
     * @return
     */
    public static boolean isAfterThirtyMinutes(Date date) {
        Calendar stdTime = Calendar.getInstance();
//        1则代表的是对年份操作，2是对月份操作，3是对星期操作，5是对日期操作，11是对小时操作，12是对分钟操作，13是对秒操作，14是对毫秒操作
        stdTime.add(12, -30);
        return (new Timestamp(stdTime.getTimeInMillis())).after(date);
    }


    /**
     * 根据日期区间获取月份列表
     *
     * @param minDate 开始时间 yyyy-MM
     * @param maxDate 结束时间 yyyy-MM
     * @return 月份列表
     * @throws Exception
     */
    public static List<String> getMonthBetween(String minDate, String maxDate, String format) throws Exception {
        ArrayList<String> result = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");

        Calendar min = Calendar.getInstance();
        Calendar max = Calendar.getInstance();

        min.setTime(sdf.parse(minDate));
        min.set(min.get(Calendar.YEAR), min.get(Calendar.MONTH), 1);

        max.setTime(sdf.parse(maxDate));
        max.set(max.get(Calendar.YEAR), max.get(Calendar.MONTH), 2);
        SimpleDateFormat sdf2 = new SimpleDateFormat(format);

        Calendar curr = min;
        while (curr.before(max)) {
            result.add(sdf2.format(curr.getTime()));
            curr.add(Calendar.MONTH, 1);
        }

        return result;
    }

    /**
     * 根据日期获取年度中的周索引[第几周]
     *
     * @param date 日期
     * @return 周索引
     * @throws Exception
     */
    public static Integer getWeekOfYear(String date) {
        Date useDate = parseString2Date(date);
        Calendar cal = Calendar.getInstance();
        cal.setTime(useDate);
        return cal.get(Calendar.WEEK_OF_YEAR);
    }

    /**
     * 根据年份获取年中周列表
     *
     * @param year 年分
     * @return 周列表
     * @throws Exception
     */
    public static Map<Integer, String> getWeeksOfYear(String year) {
        Date useDate = parseString2Date(year, "yyyy");
        Calendar cal = Calendar.getInstance();
        cal.setTime(useDate);
        //获取年中周数量
        int weeksCount = cal.getWeeksInWeekYear();
        Map<Integer, String> mapWeeks = new HashMap<>(55);
        for (int i = 0; i < weeksCount; i++) {
            cal.get(Calendar.DAY_OF_YEAR);
            mapWeeks.put(i + 1, parseDate2String(getFirstDayOfWeek(cal.get(Calendar.YEAR), i)));
        }
        return mapWeeks;
    }

    /**
     * 获取某年的第几周的开始日期
     *
     * @param year 年分
     * @param week 周索引
     * @return 开始日期
     * @throws Exception
     */
    public static Date getFirstDayOfWeek(int year, int week) {
        Calendar c = new GregorianCalendar();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, Calendar.JANUARY);
        c.set(Calendar.DATE, 1);

        Calendar cal = (GregorianCalendar) c.clone();
        cal.add(Calendar.DATE, week * 7);

        return getThisWeekSunday(cal.getTime());
    }

    /**
     * 获取某年的第几周的结束日期
     *
     * @param year 年份
     * @param week 周索引
     * @return 结束日期
     * @throws Exception
     */
    public static Date getLastDayOfWeek(int year, int week) throws Exception {
        Calendar c = new GregorianCalendar();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, Calendar.JANUARY);
        c.set(Calendar.DATE, 1);

        Calendar cal = (GregorianCalendar) c.clone();
        cal.add(Calendar.DATE, week * 7);
        return getThisWeekSunday(cal.getTime());
    }

    /**
     * 获取指定月份的最后一天  String lastDayOfMonth = getLastDayOfMonth("2019-08");
     *
     * @param yearMonth
     * @return
     */
    public static String getLastDayOfMonth(String yearMonth) {
        int year = Integer.parseInt(yearMonth.split("-")[0]);  //年
        int month = Integer.parseInt(yearMonth.split("-")[1]); //月
        Calendar cal = Calendar.getInstance();
        // 设置年份
        cal.set(Calendar.YEAR, year);
        // 设置月份
        // cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.MONTH, month); //设置当前月的上一个月
        // 获取某月最大天数
        //int lastDay = cal.getActualMaximum(Calendar.DATE);
        int lastDay = cal.getMinimum(Calendar.DATE); //获取月份中的最小值，即第一天
        // 设置日历中月份的最大天数
        //cal.set(Calendar.DAY_OF_MONTH, lastDay);
        cal.set(Calendar.DAY_OF_MONTH, lastDay - 1); //上月的第一天减去1就是当月的最后一天
        // 格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(cal.getTime());
    }


    public static String dealDateFormat(String oldDate) {
        Date date1 = null;
        SimpleDateFormat df2 = null;
        try {
            oldDate = oldDate.replace("Z", " UTC");
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS Z");
            Date date = df.parse(oldDate);
            SimpleDateFormat df1 = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.UK);
            date1 = df1.parse(date.toString());
            df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            logger.error(e.getMessage());
        }
        return df2.format(date1);
    }

    /**
     * 计算时间差
     *
     * @param startDate
     * @param endDate
     * @return 返回分钟 endDate-startDate
     */
    public static String calculationTime(String startDate, String endDate) {
        SimpleDateFormat sdf = new SimpleDateFormat(DateFormatUtil.DatePatternType.FORMAT_yyyy_MM_dd_HH_mm_ss);
        Date start = null;
        Date end = null;
        try {
            start = sdf.parse(startDate);
            end = sdf.parse(endDate);
            long cha = end.getTime() - start.getTime();
            if (cha < 0) {
                return null;
            }
            BigDecimal bigDecimal = new BigDecimal(String.valueOf(cha));
            BigDecimal divide = bigDecimal.divide(new BigDecimal(1000 * 60 * 60), 2);
            return String.valueOf(divide);
        } catch (ParseException e) {
            logger.error(e.getMessage());
            return null;
        }
    }


    /**
     * 计算两个时间差
     *
     * @param endDate 结束时间
     * @param nowDate 开始时间
     * @return 5天4小时20分钟;
     */
    public static String calculationTime(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        long sec = diff % nd % nh % nm / ns;

        return day + "天" + hour + "小时" + min + "分钟" + sec + "秒";
    }

    public static Duration between(LocalDateTime startTime, LocalDateTime endTime) {
        return Duration.between(startTime, endTime);
    }

    /**
     * 当天的开始时间
     *
     * @return
     */
    public static Date getDayStartTime() {
        LocalDateTime today_start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);//当天零点
        Date date = Date.from(today_start.atZone(ZoneId.systemDefault()).toInstant());//转Date
        return date;
    }

    /**
     * 当天的开始时间
     *
     * @return
     */
    public static String getDayStartTime2() {
        DateTimeFormatter dfDate = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.systemDefault());
        String startTime = dfDate.format(localDateTime.with(LocalTime.MIN));
        return startTime;
    }

    /**
     * 当天的结束时间
     *
     * @return
     */
    public static Date getDayEndTime() {
        //当天零点
        LocalDateTime today_end = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
        //转Date
        Date date = Date.from(today_end.atZone(ZoneId.systemDefault()).toInstant());
        return date;
    }

    /**
     * 当天的结束时间
     *
     * @return
     */
    public static String getDayEndTime2() {
        DateTimeFormatter dfDate = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.systemDefault());
        String endTime = dfDate.format(localDateTime.with(LocalTime.MAX));
        return endTime;
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * LocalDate转Date
     */
    public static Date LocalDate2Date(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault(); // 获取系统默认时区
        Instant instant = localDateTime.atZone(zoneId).toInstant(); // 将LocalDateTime转换为Instant对象
        Date date = Date.from(instant); // 将Instant对象转换为Date对象
        return date;
    }

    /**
     * Date2LocalDate
     */
    public static LocalDateTime Date2LocalDate(Date date) {

        LocalDateTime localDateTime = date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
        return localDateTime;
    }
}
